home *** CD-ROM | disk | FTP | other *** search
- /* bspmaker.cpp
-
- Win32 2D BSP tree editor and compiler.
- Tested with VC++ 2.0 running under Windows NT 3.5. */
-
- #include <windows.h>
- #include <windowsx.h>
- #include <commdlg.h>
- #include <stdio.h>
- #include <malloc.h>
- #include <math.h>
- #include "bspmaker.hpp"
-
- #define DBG 0 // set to 1 for debug checking
-
- #define MAX_NUM_VERTICES 1000
- #define MAX_NUM_LINESEGS 1000
- #define MAX_PATH_LENGTH 256
- #define NO_CHILD_TREE -1 // value for front or back tree index if there is
- // no tree in that direction
- #define MAX_INT 0x7FFFFFFF
- #define MIN_INT 0x80000000
- #define MATCH_TOLERANCE 0.00001
-
- // A vertex
- typedef struct _VERTEX
- {
- double x;
- double y;
- } VERTEX;
-
- // A potentially split piece of a line segment, as processed from the
- // base line in the original list
- typedef struct _LINESEG
- {
- _LINESEG *pnextlineseg;
- int startvertex;
- int endvertex;
- double walltop;
- double wallbottom;
- double tstart;
- double tend;
- int color;
- _LINESEG *pfronttree;
- _LINESEG *pbacktree;
- } LINESEG, *PLINESEG;
-
- static char szAppName[]="BSPMAKER";
- static HWND hwndApp;
- static int Compiling = FALSE;
- static int Width, Height;
- static LINESEG *plineseglist;
- static int numlinesegs;
- static LINESEG *pCompiledLinesegs;
- static int NumCompiledLinesegs = 0;
- static VERTEX *pvertexlist;
- static int numvertices, realnumvertices;
- static HINSTANCE hInstApp;
- static HDC hdcDraw = NULL;
- static HBITMAP hbmDraw = NULL;
- static HBITMAP hbmOld = NULL;
- static int lineListMinX, lineListMaxX;
- static int lineListMinY, lineListMaxY;
- static int VisibleCompile;
- static int DoStep = TRUE;
- static int SingleStep = 0;
- static int ExitProgram = FALSE;
- static int HaltCompile = FALSE;
- static int ClipTopLeftVertex, ClipTopRightVertex;
- static int ClipBottomLeftVertex, ClipBottomRightVertex;
-
- void DrawAllSplitters(LINESEG * pCurrentSplitter, LINESEG * pTree);
- void DrawBSPClippedLine(LINESEG *pSplit, LINESEG *pCurrentTree);
- void DisplayTree(LINESEG * pCurrentNode);
- int ShowSingleStepTree(LINESEG * pLinesegsToPartition,
- LINESEG * pSplit, LINESEG * pCurrentTree);
- void CompileBSPTree(void);
- void CleanUpObjects(void);
- void DisplayMessageBox(char * text);
- int LoadLineFile(void);
- int SaveLineFile(void);
- int WriteNode(LINESEG * ptree, LINESEG * ptreebase);
- LRESULT CALLBACK AppWndProc(HWND hwnd, UINT msg, WPARAM wParam,
- LPARAM lParam);
- void AppPaint(HWND hwnd, HDC hdc);
- LINESEG * SelectBSPTree(LINESEG * plineseghead,
- LINESEG * pCurrentTree, LINESEG ** pParentsChildPointer);
- LINESEG * BuildBSPTree(LINESEG * plineseghead, LINESEG * proot,
- LINESEG * pCurrentTree);
- #if DBG
- void CheckForBacklinks(LINESEG *plineseg);
- #define CHECK_FOR_BACKLINKS(x) CheckForBacklinks(x)
- #else
- #define CHECK_FOR_BACKLINKS(x)
- #endif //DBG
-
-
- /////////////////////////////////////////////////////////////////////
- // Load time initialization.
- /////////////////////////////////////////////////////////////////////
-
- int LoadInit(HINSTANCE hInst,HINSTANCE hPrev,int sw,LPSTR szCmdLine)
- {
- WNDCLASS cls;
-
- pvertexlist = (VERTEX *)calloc(MAX_NUM_VERTICES, sizeof(VERTEX));
- if (pvertexlist == NULL) {
- DisplayMessageBox("Couldn't get vertex storage memory");
- return FALSE;
- }
-
- plineseglist = (LINESEG *)calloc(MAX_NUM_LINESEGS,
- sizeof(LINESEG));
- if (plineseglist == NULL) {
- DisplayMessageBox("Couldn't get line segment storage memory");
- return FALSE;
- }
-
- pCompiledLinesegs = (LINESEG *)calloc(MAX_NUM_LINESEGS,
- sizeof(LINESEG));
- if (pCompiledLinesegs == NULL) {
- DisplayMessageBox("Couldn't get compiled line segment storage");
- return FALSE;
- }
-
- if (!hPrev) {
- cls.hCursor = LoadCursor(0, IDC_ARROW);
- cls.hIcon = 0;
- cls.lpszMenuName = "AppMenu";
- cls.lpszClassName = szAppName;
- cls.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
- cls.hInstance = hInst;
- cls.style = CS_BYTEALIGNCLIENT | CS_VREDRAW |
- CS_HREDRAW;
- cls.lpfnWndProc = (WNDPROC)AppWndProc;
- cls.cbClsExtra = 0;
- cls.cbWndExtra = 0;
- if (!RegisterClass(&cls))
- return FALSE;
- }
-
- hwndApp = CreateWindow (szAppName, szAppName,
- WS_OVERLAPPEDWINDOW,
- CW_USEDEFAULT, 0, 350,350, 0, 0, hInst, 0);
- HDC hdc = GetDC(hwndApp);
- if ((hdcDraw = CreateCompatibleDC(hdc)) == NULL) {
- DisplayMessageBox("Couldn't create compatible DC");
- return FALSE;
- }
- ReleaseDC(hwndApp, hdc);
-
- ShowWindow(hwndApp, sw);
-
- return TRUE;
- }
-
-
- /////////////////////////////////////////////////////////////////////
- // Main proc and message pump.
- /////////////////////////////////////////////////////////////////////
-
- int CALLBACK WinMain(HINSTANCE hInst,HINSTANCE hPrev,LPSTR szCmdLine,
- int sw)
- {
- MSG msg;
-
- hInstApp = hInst;
-
- if (!LoadInit(hInst, hPrev, sw, szCmdLine)) // initialize the app
- return FALSE;
-
- // Pump messages until quitting time, letting the idle proc
- // draw, if possible, when there's nothing else to do.
- for (;;) {
- if (PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) {
- if (msg.message == WM_QUIT)
- break;
- TranslateMessage(&msg);
- DispatchMessage(&msg);
- WaitMessage();
- }
- }
- return msg.wParam;
- }
-
-
- /////////////////////////////////////////////////////////////////////
- // Draws the current state of the world on the screen.
- /////////////////////////////////////////////////////////////////////
-
- void AppPaint(HWND hwnd, HDC hdc)
- {
- POINT pt;
- SIZE size;
- HPEN hpen;
- static int x = 0;
-
- SetMapMode(hdcDraw, MM_TEXT);
- PatBlt(hdcDraw, 0, 0, Width, Height, WHITENESS);
-
- SetMapMode(hdcDraw, MM_ANISOTROPIC);
- SetWindowExtEx(hdcDraw, lineListMaxX - lineListMinX + 1,
- (lineListMaxY - lineListMinY + 1), &size);
- SetViewportExtEx(hdcDraw, Width*9/10, -Height*9/10, &size);
- SetViewportOrgEx(hdcDraw, Width/20, Height*19/20, &pt);
- SetWindowOrgEx(hdcDraw, lineListMinX, lineListMinY, &pt);
-
- hpen = GetStockObject(BLACK_PEN);
- hpen = SelectObject(hdcDraw, hpen);
-
- double StartX, StartY, EndX, EndY;
- LINESEG *pTemp = plineseglist;
- for (int i=0; i < numlinesegs; i++, pTemp++) {
-
- StartX = pvertexlist[pTemp->startvertex].x;
- StartY = pvertexlist[pTemp->startvertex].y;
- EndX = pvertexlist[pTemp->endvertex].x;
- EndY = pvertexlist[pTemp->endvertex].y;
-
- MoveToEx(hdcDraw,
- (int)floor(StartX + (EndX - StartX) * pTemp->tstart + 0.5),
- (int)floor(StartY + (EndY - StartY) * pTemp->tstart + 0.5),
- &pt);
- LineTo(hdcDraw,
- (int)floor(StartX + (EndX - StartX) * pTemp->tend + 0.5),
- (int)floor(StartY + (EndY - StartY) * pTemp->tend + 0.5));
- }
-
- SetWindowExtEx(hdcDraw, Width, Height, &size);
- SetViewportExtEx(hdcDraw, Width, Height, &size);
- SetViewportOrgEx(hdcDraw, 0, 0, &pt);
- SetWindowOrgEx(hdcDraw, 0, 0, &pt);
- SetMapMode(hdcDraw, MM_TEXT);
-
- hpen = SelectObject(hdcDraw, hpen);
- DeleteObject(hpen);
-
- BitBlt(hdc, 0, 0, Width, Height, hdcDraw, 0, 0, SRCCOPY);
-
- GdiFlush(); // make sure this gets drawn right away
- }
-
-
- /////////////////////////////////////////////////////////////////////
- // Main window proc. Receives all messages.
- /////////////////////////////////////////////////////////////////////
-
- LRESULT CALLBACK AppWndProc(HWND hwnd, UINT msg, WPARAM wParam,
- LPARAM lParam)
- {
- PAINTSTRUCT ps;
- HDC hdc;
-
- switch (msg) {
- case WM_CREATE:
- break;
-
- case WM_COMMAND:
- switch(wParam) {
- case IDM_EXIT:
- PostMessage(hwnd, WM_CLOSE, 0, 0L);
- break;
-
- case IDM_LOAD:
- if (!LoadLineFile()) {
- CleanUpObjects();
- PostQuitMessage(0);
- } else {
- hdc = GetDC(hwnd);
- AppPaint(hwnd, hdc);
- ReleaseDC(hwnd, hdc);
- }
- break;
-
- case IDM_SAVE:
- SaveLineFile();
- break;
-
- case IDM_COMPILE:
- VisibleCompile = 0;
- CompileBSPTree();
- break;
-
- case IDM_VISIBLE_COMPILE:
- VisibleCompile = 1;
- CompileBSPTree();
- break;
-
- case IDM_SINGLE_STEP_COMPILE:
- VisibleCompile = 1;
- SingleStep = 1;
- CompileBSPTree();
- SingleStep = 0;
- break;
-
- case IDM_HALT_COMPILE:
- HaltCompile = TRUE;
- break;
- }
- return 0L;
-
- case WM_DESTROY: // clean up before leaving
- if (!Compiling) {
- CleanUpObjects();
- PostQuitMessage(0);
- } else {
- HaltCompile = TRUE;
- ExitProgram = TRUE;
- }
- break;
-
- case WM_CHAR:
- if (SingleStep) {
- // In single-step mode, a keystroke lets the next
- // step happen
- DoStep = TRUE;
- }
- break;
-
- case WM_PAINT:
- hdc = BeginPaint(hwnd, &ps);
- AppPaint (hwnd, hdc);
- EndPaint(hwnd,&ps);
- return 0L;
-
- case WM_SIZE:
- RECT rect;
- GetClientRect(hwnd, &rect);
- Width = rect.right;
- Height = rect.bottom;
-
- // Create a new compatible bitmap into which to draw
- hdc = BeginPaint(hwnd, &ps);
- HBITMAP hbm = CreateCompatibleBitmap(hdc,
- Width, Height);
- EndPaint(hwnd,&ps);
-
- if (!hbm) {
- CleanUpObjects(); // failed; fatal error
- PostQuitMessage(0);
- } else {
- hbmDraw = hbm;
- if (!hbmOld) {
- // First time; remember the original bitmap
- hbmOld = SelectObject(hdcDraw, hbm);
- } else {
- // Not first time; replace the old with the new
- hbm = SelectObject(hdcDraw, hbm);
- DeleteObject(hbm);
- }
- }
- break;
- }
- return DefWindowProc(hwnd,msg,wParam,lParam);
- }
-
-
- /////////////////////////////////////////////////////////////////////
- // Destroys the objects we have lying around.
- /////////////////////////////////////////////////////////////////////
-
- void CleanUpObjects()
- {
- if (hbmOld) {
- SelectObject(hdcDraw, hbmOld);
- DeleteObject(hbmDraw);
- }
- DeleteObject(hdcDraw);
- }
-
-
- /////////////////////////////////////////////////////////////////////
- // Loads a line file, replacing the current line set.
- // Puts up a message box and returns FALSE for fatal errors; returns
- // TRUE for success in loading, or if the user cancels. Zero-length
- // line and vertex lists may be returned.
- /////////////////////////////////////////////////////////////////////
-
- int LoadLineFile()
- {
- OPENFILENAME ofn;
- char szFile[MAX_PATH_LENGTH], szFileTitle[256];
- int i, cbString;
- char chReplace;
- char szFilter[256];
- FILE *infile;
-
- if (LoadString(hInstApp, IDS_INITLINEFILESTRING,
- szFile, sizeof(szFile)) == 0) {
- DisplayMessageBox("Init line file name load error");
- return FALSE;
- }
-
- if ((cbString = LoadString(hInstApp, IDS_LINEFILEFILTERSTRING,
- szFilter, sizeof(szFilter))) == 0) {
- DisplayMessageBox("Line file filter load error");
- return FALSE;
- }
-
- chReplace = szFilter[cbString - 1]; // retrieve wild character
- for (i = 0; szFilter[i] != '\0'; i++) {
- if (szFilter[i] == chReplace)
- szFilter[i] = '\0';
- }
-
- memset(&ofn, 0, sizeof(OPENFILENAME));
- ofn.lStructSize = sizeof(OPENFILENAME);
- ofn.hwndOwner = hwndApp;
- ofn.lpstrFilter = szFilter;
- ofn.nFilterIndex = 1;
- ofn.lpstrFile = szFile;
- ofn.nMaxFile = sizeof(szFile);
- ofn.lpstrFileTitle = szFileTitle;
- ofn.nMaxFileTitle = sizeof(szFileTitle);
- ofn.lpstrInitialDir = NULL;
- ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;
-
- TryAgain:
- if (GetOpenFileName(&ofn)) {
- //Open the file
- infile = fopen(ofn.lpstrFile, "r");
- if (infile == NULL) {
- DisplayMessageBox("Failed to open input line file");
- goto TryAgain;
- }
-
- // Read the vertex list in
- numvertices = 0;
- lineListMinX = lineListMinY = MAX_INT;
- lineListMaxX = lineListMaxY = MIN_INT;
- while ((fscanf(infile, "%lg,%lg,",
- &pvertexlist[numvertices].x,
- &pvertexlist[numvertices].y) != EOF) &&
- (pvertexlist[numvertices].x != MAX_INT)) {
-
- if (pvertexlist[numvertices].x < (double)lineListMinX) {
- lineListMinX = (int)floor(pvertexlist[numvertices].x);
- }
-
- if (pvertexlist[numvertices].x > (double)lineListMaxX) {
- lineListMaxX = (int)ceil(pvertexlist[numvertices].x);
- }
-
- if (pvertexlist[numvertices].y < (double)lineListMinY) {
- lineListMinY = (int)floor(pvertexlist[numvertices].y);
- }
-
- if (pvertexlist[numvertices].y > (double)lineListMaxY) {
- lineListMaxY = (int)ceil(pvertexlist[numvertices].y);
- }
-
- if (++numvertices >= MAX_NUM_VERTICES) {
- DisplayMessageBox("Too many vertices; emptying list");
- numvertices = 0;
- numlinesegs = 0;
- fclose(infile);
- goto TryAgain;
- }
- }
-
- if (numvertices == 0) {
- DisplayMessageBox("Vertex list is empty");
- numlinesegs = 0;
- fclose(infile);
- goto TryAgain;
- }
-
- realnumvertices = numvertices;
-
- // Do the four maximum-extent vertices
- ClipTopLeftVertex = numvertices;
- pvertexlist[ClipTopLeftVertex].x =
- (double)lineListMinX -
- (double)abs(lineListMinX - lineListMaxX) * 0.05;
- pvertexlist[ClipTopLeftVertex].y =
- (double)lineListMinY -
- (double)abs(lineListMinY - lineListMaxY) * 0.05;
- if (++numvertices >= MAX_NUM_VERTICES) {
- DisplayMessageBox("Too many vertices; emptying list");
- numvertices = realnumvertices = 0;
- numlinesegs = 0;
- fclose(infile);
- goto TryAgain;
- }
-
- ClipTopRightVertex = numvertices;
- pvertexlist[ClipTopRightVertex].x =
- (double)lineListMaxX +
- (double)abs(lineListMinX - lineListMaxX) * 0.05;
- pvertexlist[ClipTopRightVertex].y =
- (double)lineListMinY -
- (double)abs(lineListMinY - lineListMaxY) * 0.05;
- if (++numvertices >= MAX_NUM_VERTICES) {
- DisplayMessageBox("Too many vertices; emptying list");
- numvertices = realnumvertices = 0;
- numlinesegs = 0;
- fclose(infile);
- goto TryAgain;
- }
-
- ClipBottomLeftVertex = numvertices;
- pvertexlist[ClipBottomLeftVertex].x =
- (double)lineListMinX -
- (double)abs(lineListMinX - lineListMaxX) * 0.05;
- pvertexlist[ClipBottomLeftVertex].y =
- (double)lineListMaxY +
- (double)abs(lineListMinY - lineListMaxY) * 0.05;
- if (++numvertices >= MAX_NUM_VERTICES) {
- DisplayMessageBox("Too many vertices; emptying list");
- numvertices = realnumvertices = 0;
- numlinesegs = 0;
- fclose(infile);
- goto TryAgain;
- }
-
- ClipBottomRightVertex = numvertices;
- pvertexlist[ClipBottomRightVertex].x =
- (double)lineListMaxX +
- (double)abs(lineListMinX - lineListMaxX) * 0.05;
- pvertexlist[ClipBottomRightVertex].y =
- (double)lineListMaxY +
- (double)abs(lineListMinY - lineListMaxY) * 0.05;
- if (++numvertices >= MAX_NUM_VERTICES) {
- DisplayMessageBox("Too many vertices; emptying list");
- numvertices = realnumvertices = 0;
- numlinesegs = 0;
- fclose(infile);
- goto TryAgain;
- }
-
- // Read the initial line segment list in (a line segment is a
- // pair of vertices and a pair of t values)
- numlinesegs = 0;
- while ((fscanf(infile, "%d,%d, %lg,%lg, %d",
- &plineseglist[numlinesegs].startvertex,
- &plineseglist[numlinesegs].endvertex,
- &plineseglist[numlinesegs].walltop,
- &plineseglist[numlinesegs].wallbottom,
- &plineseglist[numlinesegs].color) != EOF) &&
- (plineseglist[numlinesegs].startvertex != MAX_INT)) {
-
- plineseglist[numlinesegs].pnextlineseg =
- &plineseglist[numlinesegs+1];
- plineseglist[numlinesegs].tstart = 0.0;
- plineseglist[numlinesegs].tend = 1.0;
-
- if (++numlinesegs >= MAX_NUM_LINESEGS) {
- DisplayMessageBox("Too many linesegs; emptying list");
- fclose(infile);
- numvertices = 0;
- numlinesegs = 0;
- goto TryAgain;
- }
- }
-
- if (numlinesegs == 0) {
- DisplayMessageBox("Lineseg list is empty");
- numvertices = 0;
- fclose(infile);
- goto TryAgain;
- }
-
- // Mark the end of the line segment linked list
- plineseglist[numlinesegs-1].pnextlineseg = NULL;
-
- fclose(infile); // we're done with the line file
- }
- return TRUE;
- }
-
-
- /////////////////////////////////////////////////////////////////////
- // Saves the current compiled BSP tree.
- // Puts up a message box and returns FALSE for fatal errors or if
- // the user cancels; returns TRUE for success in loading.
- /////////////////////////////////////////////////////////////////////
-
- int SaveLineFile()
- {
- OPENFILENAME ofn;
- char szFile[MAX_PATH_LENGTH], szFileTitle[256];
- FILE *outfile;
- int i, cbString;
- char chReplace;
- char szFilter[256];
-
- if (LoadString(hInstApp, IDS_INITBSPFILESTRING,
- szFile, sizeof(szFile)) == 0) {
- DisplayMessageBox("Init BSP file name load error");
- return FALSE;
- }
-
- chReplace = szFilter[cbString - 1]; // retrieve wild character
- for (i = 0; szFilter[i] != '\0'; i++) {
- if (szFilter[i] == chReplace)
- szFilter[i] = '\0';
- }
-
-
- memset(&ofn, 0, sizeof(OPENFILENAME));
- ofn.lStructSize = sizeof(OPENFILENAME);
- ofn.hwndOwner = hwndApp;
- ofn.lpstrFilter = NULL;
- ofn.nFilterIndex = 0;
- ofn.lpstrFile = szFile;
- ofn.nMaxFile = sizeof(szFile);
- ofn.lpstrFileTitle = szFileTitle;
- ofn.nMaxFileTitle = sizeof(szFileTitle);
- ofn.lpstrInitialDir = NULL;
- ofn.Flags = OFN_PATHMUSTEXIST;
-
- if (GetSaveFileName(&ofn)) {
- // Open the file
- outfile = fopen(ofn.lpstrFile, "w");
- if (outfile == NULL) {
- DisplayMessageBox("Failed to open output BSP file");
- return(FALSE);
- }
-
- //
- // Write out the header
- //
- fprintf(outfile, "2D BSP Tree Version, %d\n", VERSION);
-
- //
- // Write out the # of vertices and the vertex list, shifted left 16 bits
- // so we end up in 16.16 fixed point. Skip the extra vertices for
- // the bounding rectangle
- //
- fprintf(outfile, "------------------------------\n");
- fprintf(outfile, "Vertices section\n");
- fprintf(outfile, "------------------------------\n");
- fprintf(outfile, "# vertices:, %d\n", realnumvertices);
-
- for (i=0; i<realnumvertices; i++)
- {
- fprintf(outfile, "%ld,%ld\n",
- (long) (pvertexlist[i].x * (double)0x10000 + 0.5),
- (long) (pvertexlist[i].y * (double)0x10000 + 0.5));
- }
-
- //
- // Write out the # of nodes and the nodes
- //
- fprintf(outfile, "------------------------------\n");
- fprintf(outfile, "Nodes section\n");
- fprintf(outfile, "------------------------------\n");
- fprintf(outfile, "# nodes:, %d\n", NumCompiledLinesegs);
-
- for (i=0; i<NumCompiledLinesegs; i++)
- {
- fprintf(outfile, "%d,%d, %ld,%ld, %ld,%ld, %d,%d, %d\n",
- pCompiledLinesegs[i].startvertex,
- pCompiledLinesegs[i].endvertex,
- (long) (pCompiledLinesegs[i].walltop * (double)0x10000 + 0.5),
- (long) (pCompiledLinesegs[i].wallbottom * (double)0x10000 + 0.5),
- (long) (pCompiledLinesegs[i].tstart * (double)0x10000 + 0.5),
- (long) (pCompiledLinesegs[i].tend * (double)0x10000 + 0.5),
- pCompiledLinesegs[i].pfronttree ? pCompiledLinesegs[i].pfronttree - pCompiledLinesegs : NO_CHILD_TREE,
- pCompiledLinesegs[i].pbacktree ? pCompiledLinesegs[i].pbacktree - pCompiledLinesegs : NO_CHILD_TREE,
- pCompiledLinesegs[i].color);
- }
-
- fprintf(outfile, "------------------------------\n");
- fprintf(outfile, "End of file\n");
- fprintf(outfile, "------------------------------\n");
-
- fclose(outfile); // we're done with the line file
- }
-
- return FALSE;
- }
-
-
- /////////////////////////////////////////////////////////////////////
- // Displays a message box.
- /////////////////////////////////////////////////////////////////////
-
- void DisplayMessageBox(char *text)
- {
- static char *title = "";
-
- MessageBox(hwndApp, text, title, MB_OK);
- }
-
-
- /////////////////////////////////////////////////////////////////////
- // Compiles the current line segment list into a BSP tree.
- /////////////////////////////////////////////////////////////////////
-
- void CompileBSPTree()
- {
- LINESEG *pCompiledTree;
- LINESEG *pTemp;
- LINESEG ClipTop, ClipBottom, ClipLeft, ClipRight;
-
- if (numlinesegs == 0) {
- DisplayMessageBox("Nothing to compile");
- return;
- }
-
- // Copy the lineseg list to the compile buffer, so we don't
- // mess up the original data
- memcpy(pCompiledLinesegs, plineseglist,
- MAX_NUM_LINESEGS * sizeof(LINESEG));
-
- // Fix up all pointers except the NULL pointer at the end to
- // point into the compiled list instead of the original list.
- // Conveniently, the linesegs are always loaded in sequential
- // order and each just points to the next
- pTemp = pCompiledLinesegs;
- while (pTemp->pnextlineseg) {
- pTemp->pnextlineseg =
- (LINESEG *)((int)pTemp->pnextlineseg +
- (int)pCompiledLinesegs - (int)plineseglist);
- pTemp++;
- }
-
- // Make the initial tree the four clipping limits
- ClipTop.tstart = 0.0;
- ClipTop.tend = 1.0;
- ClipTop.startvertex = ClipTopRightVertex;
- ClipTop.endvertex = ClipTopLeftVertex;
- ClipTop.pfronttree = &ClipBottom;
- ClipTop.pbacktree = NULL;
-
- ClipBottom.tstart = 0.0;
- ClipBottom.tend = 1.0;
- ClipBottom.startvertex = ClipBottomLeftVertex;
- ClipBottom.endvertex = ClipBottomRightVertex;
- ClipBottom.pfronttree = &ClipLeft;
- ClipBottom.pbacktree = NULL;
-
- ClipLeft.tstart = 0.0;
- ClipLeft.tend = 1.0;
- ClipLeft.startvertex = ClipTopLeftVertex;
- ClipLeft.endvertex = ClipBottomLeftVertex;
- ClipLeft.pfronttree = &ClipRight;
- ClipLeft.pbacktree = NULL;
-
- ClipRight.tstart = 0.0;
- ClipRight.tend = 1.00;
- ClipRight.startvertex = ClipBottomRightVertex;
- ClipRight.endvertex = ClipTopRightVertex;
- ClipRight.pfronttree = NULL;
- ClipRight.pbacktree = NULL;
-
- NumCompiledLinesegs = numlinesegs;
-
- Compiling = TRUE;
-
- // Gray out menu items we don't want activated during compiles,
- // and activate the compile-specific ones
- EnableMenuItem(GetMenu (hwndApp), IDM_LOAD, MF_GRAYED);
- EnableMenuItem(GetMenu (hwndApp), IDM_COMPILE, MF_GRAYED);
- EnableMenuItem(GetMenu (hwndApp), IDM_VISIBLE_COMPILE, MF_GRAYED);
- EnableMenuItem(GetMenu (hwndApp), IDM_SINGLE_STEP_COMPILE,
- MF_GRAYED);
- EnableMenuItem(GetMenu (hwndApp), IDM_HALT_COMPILE,
- MF_ENABLED);
- EnableMenuItem(GetMenu (hwndApp), IDM_SAVE, MF_GRAYED);
-
- // Build the BSP tree
- if ((pCompiledTree = SelectBSPTree(pCompiledLinesegs, &ClipTop,
- &ClipRight.pfronttree)) ==
- NULL) {
- if (ExitProgram) {
- CleanUpObjects();
- PostQuitMessage(0);
- } else if (HaltCompile) {
- DisplayMessageBox("Compile halted by user");
- HaltCompile = FALSE; // reset for next time
- } else {
- DisplayMessageBox("Compile halted due to error");
- }
-
- } else {
-
- // If the root node isn't the first node in the node array,
- // swap the root node and the first node and all pointers
- // to the first node (nothing points to the root node) so
- // the root node becomes the first node, so when this tree
- // gets loaded by another program, they know the first node
- // is the root
- if (pCompiledTree != pCompiledLinesegs) {
- for (int i=0; i<NumCompiledLinesegs; i++) {
- if (pCompiledLinesegs[i].pfronttree ==
- pCompiledLinesegs) {
- pCompiledLinesegs[i].pfronttree = pCompiledTree;
- }
-
- if (pCompiledLinesegs[i].pbacktree ==
- pCompiledLinesegs) {
- pCompiledLinesegs[i].pbacktree = pCompiledTree;
- }
- }
-
- LINESEG TempLineseg = *pCompiledLinesegs;
- *pCompiledLinesegs = *pCompiledTree;
- *pCompiledTree = TempLineseg;
- }
-
- if (VisibleCompile)
- {
- EnableMenuItem(GetMenu (hwndApp), IDM_HALT_COMPILE,
- MF_GRAYED);
- EnableMenuItem(GetMenu (hwndApp), IDM_EXIT, MF_GRAYED);
- SetWindowText (hwndApp, "BSPMAKER - success - press a key");
- DoStep = FALSE;
- SingleStep = TRUE; // so the message loop knows to let us
- // out on a keypress
-
- // Get messages until we can step
- while (!DoStep) {
- MSG msg;
-
- if (PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) {
- TranslateMessage(&msg);
- DispatchMessage(&msg);
- }
- }
-
- SingleStep = FALSE;
- EnableMenuItem(GetMenu (hwndApp), IDM_EXIT, MF_ENABLED);
- SetWindowText (hwndApp, "BSPMAKER");
- }
- else
- {
- DisplayMessageBox ("Successful compile");
- }
- }
-
- // Display the original line list
- HDC hdc = GetDC(hwndApp);
- AppPaint(hwndApp, hdc);
- ReleaseDC(hwndApp, hdc);
-
- // Restore the normal menus
- EnableMenuItem(GetMenu (hwndApp), IDM_LOAD, MF_ENABLED);
- EnableMenuItem(GetMenu (hwndApp), IDM_COMPILE, MF_ENABLED);
- EnableMenuItem(GetMenu (hwndApp), IDM_VISIBLE_COMPILE,
- MF_ENABLED);
- EnableMenuItem(GetMenu (hwndApp), IDM_SINGLE_STEP_COMPILE,
- MF_ENABLED);
- EnableMenuItem(GetMenu (hwndApp), IDM_HALT_COMPILE,
- MF_GRAYED);
- EnableMenuItem(GetMenu (hwndApp), IDM_SAVE, MF_ENABLED);
-
- Compiling = FALSE;
- }
-
-
- /////////////////////////////////////////////////////////////////////
- // Builds a BSP tree from the specified line list. List must contain
- // at least one entry. If pCurrentTree is NULL, then this is the root
- // node, otherwise pCurrentTree is the tree that's been build so far.
- // Returns NULL for errors.
- /////////////////////////////////////////////////////////////////////
-
- LINESEG * SelectBSPTree(LINESEG * plineseghead,
- LINESEG * pCurrentTree, LINESEG ** pParentsChildPointer)
- {
- LINESEG *pminsplit;
- int minsplits;
- int tempsplitcount;
- LINESEG *prootline;
- LINESEG *pcurrentline;
- double nx, ny, numer, denom, t;
-
- #if DBG
- if (plineseghead == NULL) {
- DisplayMessageBox("Empty line list passed to SelectBSPTree");
- return NULL;
- }
- #endif
-
- // Pick a line as the root, and remove it from the list of lines to be
- // categorized. The line we'll select is the one of those in the list
- // that splits the fewest of the other lines in the list
- minsplits = MAX_INT;
- prootline = plineseghead;
-
- while (prootline != NULL) {
- pcurrentline = plineseghead;
- tempsplitcount = 0;
-
- while (pcurrentline != NULL) {
-
- // See how many other lines the current line splits
- nx = pvertexlist[prootline->startvertex].y -
- pvertexlist[prootline->endvertex].y;
- ny = -(pvertexlist[prootline->startvertex].x -
- pvertexlist[prootline->endvertex].x);
-
- // Calculate the dot products we'll need for line intersection
- // and spatial relationship
- numer = (nx * (pvertexlist[pcurrentline->startvertex].x -
- pvertexlist[prootline->startvertex].x)) +
- (ny * (pvertexlist[pcurrentline->startvertex].y -
- pvertexlist[prootline->startvertex].y));
- denom = ((-nx) * (pvertexlist[pcurrentline->endvertex].x -
- pvertexlist[pcurrentline->startvertex].x)) +
- ((-ny) * (pvertexlist[pcurrentline->endvertex].y -
- pvertexlist[pcurrentline->startvertex].y));
-
- // Figure out if the infinite lines of the current line and the root
- // intersect; if so, figure out if the current line segment is
- // actually split, split if so, and add front/back polygons as
- // appropriate
- if (denom == 0.0) {
-
- // No intersection, because lines are parallel; no split,
- // so nothing to do
-
- } else {
-
- // Infinite lines intersect; figure out whether the actual line segment intersects the infinite line
- // of the root, and split if so
- t = numer / denom;
-
- if ((t > pcurrentline->tstart) &&
- (t < pcurrentline->tend)) {
- // The root splits the current line
- tempsplitcount++;
- } else {
- // Intersection outside segment limits, so no split,
- // nothing to do
- }
- }
-
- pcurrentline = pcurrentline->pnextlineseg;
- }
-
- if (tempsplitcount < minsplits) {
- pminsplit = prootline;
- minsplits = tempsplitcount;
- }
-
- prootline = prootline->pnextlineseg;
- }
-
- // For now, make this a leaf node so we can traverse the tree
- // as it is at this point. BuildBSPTree() will add children as
- // appropriate
- pminsplit->pfronttree = NULL;
- pminsplit->pbacktree = NULL;
-
- // Point the parent's child pointer to this node, so we can
- // track the currently-build tree
- *pParentsChildPointer = pminsplit;
-
- // Show the current state of building the BSP tree, if desired
- if (VisibleCompile) {
- if (!ShowSingleStepTree(plineseghead, pminsplit,
- pCurrentTree)) {
- return NULL;
- }
- }
-
- return BuildBSPTree(plineseghead, pminsplit, pCurrentTree);
- }
-
-
- /////////////////////////////////////////////////////////////////////
- // Builds a BSP tree given the specified root, by creating front and back
- // lists from the remaining lines, then calling itself recursively
- /////////////////////////////////////////////////////////////////////
-
- LINESEG * BuildBSPTree(LINESEG * plineseghead, LINESEG * prootline,
- LINESEG * pCurrentTree)
- {
- LINESEG *pfrontlines;
- LINESEG *pbacklines;
- LINESEG *pcurrentline;
- LINESEG *pnextlineseg;
- LINESEG *psplitline;
- double nx, ny, numer, denom, t;
- int Done;
-
- //
- // Categorize all non-root lines as either in front of the
- // root's infinite line, behind the root's infinite line, or split by
- // the root's infinite line, in which case we split it into two lines
- //
- pfrontlines = NULL;
- pbacklines = NULL;
-
- pcurrentline = plineseghead;
-
- while (pcurrentline != NULL)
- {
- CHECK_FOR_BACKLINKS(pfrontlines);
- CHECK_FOR_BACKLINKS(pbacklines);
-
- //
- // Skip the root line when encountered
- //
- if (pcurrentline == prootline) {
- pcurrentline = pcurrentline->pnextlineseg;
- } else {
- nx = pvertexlist[prootline->startvertex].y -
- pvertexlist[prootline->endvertex].y;
- ny = -(pvertexlist[prootline->startvertex].x -
- pvertexlist[prootline->endvertex].x);
-
- //
- // Calculate the dot products we'll need for line intersection and
- // spatial relationship
- //
- numer = (nx * (pvertexlist[pcurrentline->startvertex].x -
- pvertexlist[prootline->startvertex].x)) +
- (ny * (pvertexlist[pcurrentline->startvertex].y -
- pvertexlist[prootline->startvertex].y));
- denom = ((-nx) * (pvertexlist[pcurrentline->endvertex].x -
- pvertexlist[pcurrentline->startvertex].x)) +
- (-(ny) * (pvertexlist[pcurrentline->endvertex].y -
- pvertexlist[pcurrentline->startvertex].y));
-
- //
- // Figure out if the infinite lines of the current line and the root
- // intersect; if so, figure out if the current line segment is
- // actually split, split if so, and add front/back polygons as
- // appropriate
- //
- if (denom == 0.0) {
- //
- // No intersection, because lines are parallel; just add to
- // appropriate list
- //
- pnextlineseg = pcurrentline->pnextlineseg;
-
- if (numer < 0.0) {
-
- // Current line is in front of root line; link into
- // front list
- pcurrentline->pnextlineseg = pfrontlines;
- pfrontlines = pcurrentline;
-
- } else {
-
- // Current line is behind root line; link into back list
- pcurrentline->pnextlineseg = pbacklines;
- pbacklines = pcurrentline;
-
- }
-
- pcurrentline = pnextlineseg;
-
- } else {
-
- // Infinite lines intersect; figure out whether the actual line
- // segment intersects the infinite line of the root, and split
- // if so
- t = numer / denom;
-
- if ((t > pcurrentline->tstart) &&
- (t < pcurrentline->tend)) {
-
- // The line segment must be split; add one split segment to
- // each list
- if (NumCompiledLinesegs > (MAX_NUM_LINESEGS - 1)) {
- DisplayMessageBox("Out of space for line segments; "
- "increase MAX_NUM_LINESEGS");
- return NULL;
-
- }
-
- // Make a new line entry for the split part of the line
- psplitline = &pCompiledLinesegs[NumCompiledLinesegs];
- NumCompiledLinesegs++;
-
- *psplitline = *pcurrentline;
- psplitline->tstart = t;
- pcurrentline->tend = t;
-
- pnextlineseg = pcurrentline->pnextlineseg;
-
- if (numer < 0.0) {
-
- // Presplit part is in front of root line; link
- // into front list and put postsplit part in back
- // list
- pcurrentline->pnextlineseg = pfrontlines;
- pfrontlines = pcurrentline;
-
- psplitline->pnextlineseg = pbacklines;
- pbacklines = psplitline;
-
- } else {
-
- // Presplit part is in back of root line; link
- // into back list and put postsplit part in front
- // list
- psplitline->pnextlineseg = pfrontlines;
- pfrontlines = psplitline;
-
- pcurrentline->pnextlineseg = pbacklines;
- pbacklines = pcurrentline;
-
- }
-
- pcurrentline = pnextlineseg;
-
- } else {
-
- // Intersection outside segment limits, so no need to split;
- // just add to proper list
- pnextlineseg = pcurrentline->pnextlineseg;
-
- Done = 0;
- while (!Done) {
-
- if (numer < -MATCH_TOLERANCE) {
-
- // Current line is in front of root line;
- // link into front list
- pcurrentline->pnextlineseg = pfrontlines;
- pfrontlines = pcurrentline;
- Done = 1;
-
- } else if (numer > MATCH_TOLERANCE) {
-
- // Current line is behind root line; link into back list
- pcurrentline->pnextlineseg = pbacklines;
- pbacklines = pcurrentline;
- Done = 1;
-
- } else {
-
- // The point on the current line we picked to do front/back
- // evaluation happens to be collinear with the root, so use
- // the other end of the current line and try again
- numer = (nx * (pvertexlist[pcurrentline->endvertex].x -
- pvertexlist[prootline->startvertex].x)) +
- (ny * (pvertexlist[pcurrentline->endvertex].y -
- pvertexlist[prootline->startvertex].y));
- }
- }
-
- pcurrentline = pnextlineseg;
- }
- }
- }
- }
-
- CHECK_FOR_BACKLINKS(pfrontlines);
- CHECK_FOR_BACKLINKS(pbacklines);
-
- // Make a node out of the root line, with the front and back trees
- // attached
- if (pfrontlines == NULL) {
- prootline->pfronttree = NULL;
- } else {
- if (!SelectBSPTree(pfrontlines, pCurrentTree,
- &prootline->pfronttree)) {
- return NULL;
- }
- }
-
- if (pbacklines == NULL) {
- prootline->pbacktree = NULL;
- } else {
- if (!SelectBSPTree(pbacklines, pCurrentTree,
- &prootline->pbacktree)) {
- return NULL;
- }
- }
-
- return(prootline);
- }
-
-
- #if DBG
-
- /////////////////////////////////////////////////////////////////////
- // Finds any backlinks (these are always errors) in the line segment
- // list.
- /////////////////////////////////////////////////////////////////////
-
- void CheckForBacklinks(LINESEG *plineseg)
- {
- LINESEG *p1,*p2;
-
- if ((plineseg == NULL) || (plineseg->pnextlineseg == NULL))
- {
- return;
- }
-
- p1 = plineseg;
- p2 = plineseg->pnextlineseg;
-
- while ((p1 != NULL) && (p2 != NULL))
- {
- if (p1 == p2)
- {
- DisplayMessageBox("Backlink");
- return;
- }
- p1 = p1->pnextlineseg;
- p2 = p2->pnextlineseg;
- if (p2 == NULL)
- {
- return;
- }
- p2 = p2->pnextlineseg;
- }
- }
- #endif //DBG
-
-
- /////////////////////////////////////////////////////////////////////
- // Shows the current splitting line in the tree. Returns TRUE for
- // success, FALSE for failure.
- /////////////////////////////////////////////////////////////////////
-
- int ShowSingleStepTree(LINESEG * pLinesegsToPartition,
- LINESEG * pSplit, LINESEG * pCurrentTree)
- {
- HPEN hpen, hpenOld;
- POINT pt;
- SIZE size;
- HDC hdc;
- double StartX, StartY, EndX, EndY;
- LINESEG *pTemp;
-
- hdc = GetDC(hwndApp);
-
- PatBlt(hdcDraw, 0, 0, Width, Height, WHITENESS);
-
- SetMapMode(hdcDraw, MM_ANISOTROPIC);
- SetWindowExtEx(hdcDraw, lineListMaxX - lineListMinX + 1,
- lineListMaxY - lineListMinY + 1, &size);
- SetViewportExtEx(hdcDraw, Width*9/10, -Height*9/10, &size);
- SetViewportOrgEx(hdcDraw, Width/20, Height*19/20, &pt);
- SetWindowOrgEx(hdcDraw, lineListMinX, lineListMinY, &pt);
-
- // First, draw all the lines there are, in black
- hpenOld = SelectObject(hdcDraw, GetStockObject(BLACK_PEN));
-
- pTemp = pCompiledLinesegs;
- for (int i=0; i < NumCompiledLinesegs; i++, pTemp++) {
-
- StartX = pvertexlist[pTemp->startvertex].x;
- StartY = pvertexlist[pTemp->startvertex].y;
- EndX = pvertexlist[pTemp->endvertex].x;
- EndY = pvertexlist[pTemp->endvertex].y;
-
- MoveToEx(hdcDraw,
- (int)floor(StartX + (EndX - StartX) * pTemp->tstart + 0.5),
- (int)floor(StartY + (EndY - StartY) * pTemp->tstart + 0.5),
- &pt);
- LineTo(hdcDraw,
- (int)floor(StartX + (EndX - StartX) * pTemp->tend + 0.5),
- (int)floor(StartY + (EndY - StartY) * pTemp->tend + 0.5));
- }
-
- // Draw the lines being partitioned, in red
- hpen = CreatePen(PS_SOLID, 0, RGB(0xFF, 0x00, 0x00));
- DeleteObject(SelectObject(hdcDraw, hpen));
- pTemp = pLinesegsToPartition;
- while (pTemp) {
-
- StartX = pvertexlist[pTemp->startvertex].x;
- StartY = pvertexlist[pTemp->startvertex].y;
- EndX = pvertexlist[pTemp->endvertex].x;
- EndY = pvertexlist[pTemp->endvertex].y;
-
- MoveToEx(hdcDraw,
- (int)floor(StartX + (EndX - StartX) * pTemp->tstart + 0.5),
- (int)floor(StartY + (EndY - StartY) * pTemp->tstart + 0.5),
- &pt);
- LineTo(hdcDraw,
- (int)floor(StartX + (EndX - StartX) * pTemp->tend + 0.5),
- (int)floor(StartY + (EndY - StartY) * pTemp->tend + 0.5));
-
- pTemp = pTemp->pnextlineseg;
- }
-
- // Draw the line segment we're splitting along in cyan, and
- // make it fat
- hpen = CreatePen(PS_SOLID, 10, RGB(0x00, 0xFF, 0xFF));
- DeleteObject(SelectObject(hdcDraw, hpen));
-
- StartX = pvertexlist[pSplit->startvertex].x;
- StartY = pvertexlist[pSplit->startvertex].y;
- EndX = pvertexlist[pSplit->endvertex].x;
- EndY = pvertexlist[pSplit->endvertex].y;
-
- MoveToEx(hdcDraw,
- (int)floor(StartX + (EndX - StartX) *
- pSplit->tstart + 0.5),
- (int)floor(StartY + (EndY - StartY) *
- pSplit->tstart + 0.5),
- &pt);
- LineTo(hdcDraw,
- (int)floor(StartX + (EndX - StartX) * pSplit->tend + 0.5),
- (int)floor(StartY + (EndY - StartY) *
- pSplit->tend + 0.5));
-
- // Walk the current tree and draw in dotted gray the full
- // spanning length of all lines currently in it, so the way the
- // tree carves up the world is visible
- hpen = CreatePen(PS_DOT, 0, RGB(0xC0, 0xC0, 0xC0));
- DeleteObject(SelectObject(hdcDraw, hpen));
-
- DrawAllSplitters(pCurrentTree, pCurrentTree);
-
- // Draw the lines already in the tree, in white to erase anything
- // that's already there, then in dotted green
- hpen = CreatePen(PS_SOLID, 0, RGB(0xFF, 0xFF, 0xFF));
- DeleteObject(SelectObject(hdcDraw, hpen));
- DisplayTree(pCurrentTree);
- hpen = CreatePen(PS_DOT, 0, RGB(0x00, 0xFF, 0x00));
- DeleteObject(SelectObject(hdcDraw, hpen));
- DisplayTree(pCurrentTree);
-
- // Draw the whole splitting line, spanning the area being split,
- // in dotted black
- hpen = CreatePen(PS_DOT, 0, RGB(0x00, 0x00, 0x00));
- DeleteObject(SelectObject(hdcDraw, hpen));
-
- DrawBSPClippedLine(pSplit, pCurrentTree);
-
- DeleteObject(SelectObject(hdcDraw, hpenOld));
-
- SetWindowExtEx(hdcDraw, Width, Height, &size);
- SetViewportExtEx(hdcDraw, Width, Height, &size);
- SetViewportOrgEx(hdcDraw, 0, 0, &pt);
- SetWindowOrgEx(hdcDraw, 0, 0, &pt);
- SetMapMode(hdcDraw, MM_TEXT);
-
- BitBlt(hdc, 0, 0, Width, Height, hdcDraw, 0, 0, SRCCOPY);
-
- ReleaseDC(hwndApp, hdc);
-
- GdiFlush(); // make sure this gets drawn right away
-
- // When single-stepping, we have to wait for the message loop to
- // detect a keypress and set DoStep to TRUE. DoStep is always
- // left TRUE except for this case, so non-single-stepping
- // operation will only cause the loop below to repeat until the
- // message queue is cleared
- if (SingleStep) {
- DoStep = FALSE;
- }
-
- MSG msg;
-
- // Get messages until we can step
- while (!DoStep) {
- if (PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) {
- TranslateMessage(&msg);
- DispatchMessage(&msg);
-
- // If this message caused the exit flag to be set, done
- if (HaltCompile) {
- DoStep = TRUE;
- return FALSE;
- }
- }
- }
- return TRUE;
- }
-
-
- /////////////////////////////////////////////////////////////////////
- // Draws the line segments for all nodes in the current tree by
- // calling itself recursively. Pen should be set on entry.
- /////////////////////////////////////////////////////////////////////
-
- void DisplayTree(LINESEG * pCurrentNode)
- {
- double StartX, StartY, EndX, EndY;
- POINT pt;
-
- StartX = pvertexlist[pCurrentNode->startvertex].x;
- StartY = pvertexlist[pCurrentNode->startvertex].y;
- EndX = pvertexlist[pCurrentNode->endvertex].x;
- EndY = pvertexlist[pCurrentNode->endvertex].y;
-
- MoveToEx(hdcDraw,
- (int)floor(StartX +
- (EndX - StartX) * pCurrentNode->tstart + 0.5),
- (int)floor(StartY +
- (EndY - StartY) * pCurrentNode->tstart + 0.5),
- &pt);
- LineTo(hdcDraw,
- (int)floor(StartX +
- (EndX - StartX) * pCurrentNode->tend + 0.5),
- (int)floor(StartY +
- (EndY - StartY) * pCurrentNode->tend + 0.5));
-
- if (pCurrentNode->pfronttree) {
- DisplayTree(pCurrentNode->pfronttree);
- }
-
- if (pCurrentNode->pbacktree) {
- DisplayTree(pCurrentNode->pbacktree);
- }
- }
-
-
- /////////////////////////////////////////////////////////////////////
- // Draws the whole line for lineseg pSplit, clipped to the area of
- // pCurrentTree that pSplit is in. Pen should be set on entry.
- /////////////////////////////////////////////////////////////////////
-
- void DrawBSPClippedLine(LINESEG *pSplit, LINESEG *pCurrentTree)
- {
- double StartX, StartY, EndX, EndY;
- double ClippedTStart, ClippedTEnd;
- double LinesegTStart, LinesegTEnd;
- double nx, ny, numer, denom, t;
- LINESEG *pCurrentNode;
- POINT pt;
-
- StartX = pvertexlist[pSplit->startvertex].x;
- StartY = pvertexlist[pSplit->startvertex].y;
- EndX = pvertexlist[pSplit->endvertex].x;
- EndY = pvertexlist[pSplit->endvertex].y;
- LinesegTStart = pSplit->tstart;
- LinesegTEnd = pSplit->tend;
-
- // Make the line longer than any line can be, so we don't miss
- // anything as we clip it
- ClippedTStart = -1000000.0;
- ClippedTEnd = 1000000.0;
-
- // Clip the line to the tree, keeping what's on the same side as
- // the pSplit lineseg and descending in that direction. Stop if
- // we get to a node that's the pSplit lineseg; that means we've
- // done all the clipping there is to do
- pCurrentNode = pCurrentTree;
- while ((pCurrentNode != pSplit) && (pCurrentNode != NULL)) {
-
- nx = pvertexlist[pCurrentNode->startvertex].y -
- pvertexlist[pCurrentNode->endvertex].y;
- ny = -(pvertexlist[pCurrentNode->startvertex].x -
- pvertexlist[pCurrentNode->endvertex].x);
-
- // Calculate the dot products we'll need for line
- // intersection and spatial relationship
-
- //*** can just store the pSplit values once for this***
-
- numer = (nx * (pvertexlist[pSplit->startvertex].x -
- pvertexlist[pCurrentNode->startvertex].x)) +
- (ny * (pvertexlist[pSplit->startvertex].y -
- pvertexlist[pCurrentNode->startvertex].y));
- denom = ((-nx) * (pvertexlist[pSplit->endvertex].x -
- pvertexlist[pSplit->startvertex].x)) +
- ((-ny) * (pvertexlist[pSplit->endvertex].y -
- pvertexlist[pSplit->startvertex].y));
-
- // Figure out if the infinite lines of the current line and
- // the root intersect; if so, figure out if the current line
- // segment is actually split, split if so, and add front/back
- // polygons as appropriate
- if (denom == 0.0) {
- // No intersection, because lines are parallel; no need
- // to do anything except descend in the right direction
- if (numer < 0.0) {
-
- // Splitting line is in front of this clip line, so
- // descend to front
- pCurrentNode = pCurrentNode->pfronttree;
-
- } else {
-
- // Splitting line is behind this clip line, so descend
- // to back
- pCurrentNode = pCurrentNode->pbacktree;
-
- }
-
- } else {
-
- // Infinite lines intersect; figure out whether the
- // infinite clipping line intersects the actual splitting
- // line segment, and clip if so
- t = numer / denom;
-
- if ((t > ClippedTStart) && (t < ClippedTEnd)) {
-
- // The tree can't clip the actual lineseg (which has
- // already been clipped to the tree), so a single
- // comparison tells us which end of the splitting
- // line just got clipped
- if (t <= LinesegTStart) {
- ClippedTStart = t;
- } else {
- ClippedTEnd = t;
- }
-
- }
-
- // Walk to the child tree on the side that the
- // remaining part of the splitting line is on
- int Done = FALSE;
-
- // Recalculate based on the actual endpoints of the
- // line segment for which we're clipping the infinite
- // line, because we're sure that both line segment
- // endpoints are already fully clipped to the tree
- double FullyClippedX = StartX +
- (EndX - StartX) * LinesegTStart;
- double FullyClippedY = StartY +
- (EndY - StartY) * LinesegTStart;
-
- do {
-
- numer = (nx * (FullyClippedX -
- pvertexlist[pCurrentNode->startvertex].x)) +
- (ny * (FullyClippedY -
- pvertexlist[pCurrentNode->startvertex].y));
-
- if (numer < -MATCH_TOLERANCE) {
-
- pCurrentNode = pCurrentNode->pfronttree;
- Done = TRUE;
-
- } else if (numer > MATCH_TOLERANCE) {
-
- pCurrentNode = pCurrentNode->pbacktree;
- Done = TRUE;
-
- } else {
-
- // The point on the splitting line we picked to do
- // front/back evaluation happens to be collinear with
- // the current clipping line, so use the other end of
- // the splitting line and try again
- FullyClippedX = StartX +
- (EndX - StartX) * LinesegTEnd;
- FullyClippedY = StartY +
- (EndY - StartY) * LinesegTEnd;
- }
- } while (!Done);
- }
- }
-
- // Draw the splitting line across the area it splits
- if ((ClippedTStart != 1000000.0) && (ClippedTEnd != 1000000.0)) {
- MoveToEx(hdcDraw,
- (int)floor(StartX +
- (EndX - StartX) * ClippedTStart + 0.5),
- (int)floor(StartY +
- (EndY - StartY) * ClippedTStart + 0.5),
- &pt);
- LineTo(hdcDraw,
- (int)floor(StartX + (EndX - StartX) * ClippedTEnd + 0.5),
- (int)floor(StartY + (EndY - StartY) * ClippedTEnd + 0.5));
- }
- }
-
-
- /////////////////////////////////////////////////////////////////////
- // Draws all splitting lines in the tree pTree starting at
- // pCurrentSplitter, each clipped by the higher levels of the tree.
- /////////////////////////////////////////////////////////////////////
-
- void DrawAllSplitters(LINESEG *pCurrentSplitter, LINESEG * pTree)
- {
- DrawBSPClippedLine(pCurrentSplitter, pTree);
- if (pCurrentSplitter->pfronttree) {
- DrawAllSplitters(pCurrentSplitter->pfronttree, pTree);
- }
- if (pCurrentSplitter->pbacktree) {
- DrawAllSplitters(pCurrentSplitter->pbacktree, pTree);
- }
- }
-